package model

import (
	"fmt"
	"gowork/checkWorkAttendance/utils"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"
)

type Staff struct {
	Id int64 `xorm:"pk autoincr"`
	Name string
	Wages float64
	CreatedAt time.Time
	UpdatedAt time.Time
}

type StaffMonthlySummary struct {
	Id int64 `xorm:"pk autoincr"`
	StaffId int64
	Month string
	SumLateTime int
	SumLeaveEarlyTime int
	SumCardShortage int
	SumMiner float64
	MonthWages float64
	SumAbsent int
	CreatedAt time.Time
	UpdatedAt time.Time
}

type IncomingParameters struct {
	StaffId int64
	ScheduleTimeList []*SchedulingDate
}

type SchedulingDate struct {
	Date string
	ClockInNormally time.Time
	ClockOutNormally time.Time
	State bool
}

type SchedulingInformation struct {
	Id int64 `xorm:"pk autoincr"`
	StaffId int64
	ScheduleDate string
	State bool
	ClockInNormally time.Time
	ClockOutNormally time.Time
	ClockInState int
	ClockInTime time.Time
	ClockOutState int
	ClockOutTime time.Time
	LateTime int  //迟到
	LeaveEarlyTime int   //早退
	CardShortage int   //缺卡
	Miner float64    //矿工
	WorkOvertime float64   //加班
	Absent int  //缺勤
	LeaveRecord interface{} `xorm:"-"`
	MarkedRed bool `xorm:"-"`
	CreatedAt time.Time
	UpdatedAt time.Time
}

type AbnormalRecord struct {
	Id int64 `xorm:"pk autoincr"`
	Type int
	AbsenceLength int64
	ProcessingTime int64
	UntreatedDuration int64
	CreatedAt time.Time
	UpdatedAt time.Time
}

type LeaveRecord struct {
	Id int64 `xorm:"pk autoincr"`
	StaffId int64
	Reasons string
	StartTime time.Time
	EndTime time.Time
	Duration int
	CreatedAt time.Time
	UpdatedAt time.Time
}

type OvertimeApplicationRecord struct {
	Id int64 `xorm:"pk autoincr"`
	StaffId int64
	OvertimeDate string
	StartTime time.Time
	EndTime time.Time
	Duration float64
	CreatedAt time.Time
	UpdatedAt time.Time
}

func CountMonthDay(year int, month int) (days int) {
	if month != 2 {
		if month == 4 || month == 6 || month == 9 || month == 11 {
			days = 30
		} else {
			days = 31
			fmt.Fprintln(os.Stdout, "The month has 31 days");
		}
	} else {
		if (((year % 4) == 0 && (year % 100) != 0) || (year % 400) == 0) {
			days = 29
		} else {
			days = 28
		}
	}
	fmt.Fprintf(os.Stdout, "The %d-%d has %d days.\n", year, month, days)
	return
}

func (staff *Staff)InsertScheduling(ip *IncomingParameters) (map[string]interface{}, error){

	w := new(utils.Worker)

	for _, stlv := range ip.ScheduleTimeList{
		var sif SchedulingInformation
		sif.Id = w.GetId()
		sif.StaffId = ip.StaffId
		sif.ScheduleDate = stlv.Date
		sif.State = stlv.State
		sif.ClockInNormally = stlv.ClockInNormally
		sif.ClockOutNormally = stlv.ClockOutNormally
		sif.ClockInState = 1
		sif.ClockOutState = 1
		sif.CreatedAt = time.Now()
		sif.UpdatedAt = time.Now()
		x.Insert(&sif)
	}

	return map[string]interface{}{"message":"成功", "code":200}, nil
}

func (staff *Staff)QueryScheduling(sId int64, d string) ([]*SchedulingInformation, error){

	var sif []*SchedulingInformation
	x.Where("staff_id = ?", sId).And("schedule_date like ?", d+"%").Find(&sif)
	for _, s := range sif{
		if s.Absent > 0{
			s.MarkedRed = true
		}

		var lr LeaveRecord
		x.SQL("select * from leave_record where start_time <= ? and end_time >= ?", s.ScheduleDate+" 09:00:00", s.ScheduleDate+" 09:00:00").Get(&lr)
		s.LeaveRecord = &lr
	}

	return sif, nil
}

func (staff *Staff)PunchClock(sId int64, d string)(map[string]interface{}, error){

	var si SchedulingInformation
	x.Where("staff_id = ?", sId).And("schedule_date = ?", d).Get(&si)

	now := time.Now()

	if si.State == true{

		loadLocation, lErr := time.LoadLocation("Asia/Shanghai")
		if lErr != nil {
			return map[string]interface{}{"code": 400, "mesage": "失败"}, lErr
		}
		tt, err := time.ParseInLocation("2006-01-02 15:04:05", d+" 12:00:00", loadLocation)
		if err != nil {
			return map[string]interface{}{"code": 400, "mesage": "失败"}, err
		}

		fmt.Println("tt：", tt)
		fmt.Println("now：", now)
		if now.After(tt) == true{

			si.ClockOutTime = now
			si.ClockOutState = 2
			x.Id(si.Id).Update(&si)

		}else {

			si.ClockInTime = now
			si.ClockInState = 2
			x.Id(si.Id).Update(&si)
		}

	}else {

		if si.ClockInState == 1 {
			si.ClockInState = 2
			si.ClockInTime = now
			x.Id(si.Id).Update(&si)

		}else if si.ClockInState == 2 && si.ClockOutState == 1 {
			si.ClockOutState = 2
			si.ClockOutTime = now
			x.Id(si.Id).Update(&si)

		}else if si.ClockInState == 2 && si.ClockOutState == 2 {
			si.ClockInTime = now
			x.Id(si.Id).Update(&si)
		}
	}

	return map[string]interface{}{"code":200, "message":"成功"}, nil

}

func (staff *Staff)SingleDayStatistics(sId int64, day string)(map[string]interface{}, error){

	var si SchedulingInformation
	x.Where("staff_id = ?", sId).And("schedule_date = ?", day).Get(&si)

	if si.State == true {
		if si.ClockInState == 2 {

			differenceIn := si.ClockInTime.Sub(si.ClockInNormally)

			muIn := differenceIn.Minutes()
			fmt.Println("muIt：", muIn)
			if muIn > 5 && muIn < 30 {
				si.LateTime += int(muIn)
				si.Absent += int(muIn)
			} else if muIn >= 30 {
				si.Miner += 0.5
				si.Absent += 4
			}

		} else {

			si.Miner += 0.5
			si.Absent += 4
			si.CardShortage += 1
		}

		if si.ClockOutState == 2 {

			differenceOut := si.ClockOutNormally.Sub(si.ClockOutTime)

			muOut := differenceOut.Minutes()

			if muOut > 5 && muOut < 30 {
				si.LeaveEarlyTime += int(muOut)
				si.Absent += int(muOut)
			} else if muOut >= 30 {
				si.Miner += 0.5
				si.Absent += 4
			}

		} else {

			si.Miner += 0.5
			si.Absent += 4
			si.CardShortage += 1
		}

		x.Id(si.Id).Update(&si)
	}

	return map[string]interface{}{"code":200, "message":"成功"}, nil
}

func (staff *Staff)Leave(sId int64, st time.Time, et time.Time, dl int, r string, day string)(map[string]interface{}, error){

	w := new(utils.Worker)

	var lr = LeaveRecord{
		Id:        w.GetId(),
		StaffId: sId,
		Reasons:   r,
		StartTime: st,
		EndTime:   et,
		Duration:  dl,
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}
	x.Insert(&lr)

	loadLocation, _ := time.LoadLocation("Asia/Shanghai")
	startTime, _ := time.ParseInLocation("2006-01-02", st.Format("2006-01-02"), loadLocation)
	endTime, _ := time.ParseInLocation("2006-01-02", et.Format("2006-01-02"), loadLocation)
	for {
		if startTime.Unix() > endTime.Unix(){
			break
		}

		var si SchedulingInformation
		x.Where("staff_id = ?", sId).And("schedule_date = ?", startTime.Format("2006-01-02")).Get(&si)

		if dl >= si.Absent{

			dl -= si.Absent

			si.LateTime = 0
			si.LeaveEarlyTime = 0
			si.Absent = 0

			x.Id(si.Id).Cols("late_time", "leave_early_time", "absent").Update(&si)

			fmt.Println("dl：", dl)

		}else {
			si.Absent -= dl

			fmt.Println("dl：", dl)

			x.Id(si.Id).Update(&si)

			break
		}

		startTime = startTime.AddDate(0, 0, 1)
	}


	//var si SchedulingInformation
	//x.Where("staff_id = ?", sId).And("schedule_date = ?", day).Get(&si)

	//if dl >= si.Absent{
	//	si.LateTime = 0
	//	si.LeaveEarlyTime = 0
	//	si.Absent = 1
	//}else {
	//	si.Absent -= dl
	//}
	//
	//x.Id(si.Id).Update(&si)

	return map[string]interface{}{"code":200, "message":"成功"}, nil
}

func (staff *Staff)OvertimeApplication(oar *OvertimeApplicationRecord)(map[string]interface{}, error){

	w := new(utils.Worker)

	var si SchedulingInformation
	x.Where("staff_id = ?", oar.StaffId).And("schedule_date = ?", oar.OvertimeDate).Get(&si)
	if (float64(si.ClockOutTime.Unix()) - float64(si.ClockOutNormally.Unix()))/3600 < oar.Duration{
		return map[string]interface{}{"code":400, "message":"超过正确的加班时间"}, nil
	}

	oar.Id = w.GetId()
	oar.CreatedAt = time.Now()
	oar.UpdatedAt = time.Now()
	x.Insert(oar)
	return map[string]interface{}{"code":200, "message":"成功"}, nil
}

func (staff *Staff)MonthlySummary(m string)(map[string]interface{}, error){

	w := new(utils.Worker)

	var sList []*Staff
	x.Find(&sList)

	for _, s := range sList{

		var sms = StaffMonthlySummary{
			Id:                w.GetId(),
			StaffId:           s.Id,
			Month:             m,
			CreatedAt:         time.Now(),
			UpdatedAt:         time.Now(),
		}

		var siList []*SchedulingInformation
		x.Where("staff_id = ?", s.Id).And("schedule_date like ?", m+"%").Find(&siList)
		for _, si := range siList{

			sms.SumLateTime += si.LateTime
			sms.SumLeaveEarlyTime += si.LeaveEarlyTime
			sms.SumCardShortage += si.CardShortage
			sms.SumMiner += si.Miner
			sms.SumAbsent += si.Absent

		}

		t := strings.Split(m, "-")

		y, oErr := strconv.Atoi(t[0])
		if oErr != nil{
			return map[string]interface{}{"code":400, "message":oErr.Error()}, nil
		}

		n, wErr := strconv.Atoi(t[1])
		if wErr != nil{
			return map[string]interface{}{"code":400, "message":wErr.Error()}, nil
		}

		sms.MonthWages = s.Wages - float64(sms.SumLateTime + sms.SumLeaveEarlyTime)*10 - sms.SumMiner*(s.Wages/float64(CountMonthDay(y, n))) - float64(sms.SumCardShortage*50)

		x.Insert(&sms)
	}

	return map[string]interface{}{"code":200, "message":"成功"}, nil

}

func (staff *Staff)MonthlySummaryThred(m string)(map[string]interface{}, error){

	w := new(utils.Worker)

	var sList []*Staff
	x.Find(&sList)

	var wg sync.WaitGroup
	wg.Add(2)
	for i := 0; i < 2; i++ {
		var finalI int = i
		go func() (map[string]interface{}, error){
			for _, s := range sList[finalI:(finalI+1)] {

				var sms = StaffMonthlySummary{
					Id:        w.GetId(),
					StaffId:   s.Id,
					Month:     m,
					CreatedAt: time.Now(),
					UpdatedAt: time.Now(),
				}

				var siList []*SchedulingInformation
				x.Where("staff_id = ?", s.Id).And("schedule_date like ?", m+"%").Find(&siList)
				for _, si := range siList {

					sms.SumLateTime += si.LateTime
					sms.SumLeaveEarlyTime += si.LeaveEarlyTime
					sms.SumCardShortage += si.CardShortage
					sms.SumMiner += si.Miner
					sms.SumAbsent += si.Absent

				}

				t := strings.Split(m, "-")

				y, oErr := strconv.Atoi(t[0])
				if oErr != nil {
					return map[string]interface{}{"code": 400, "message": oErr.Error()}, nil
				}

				n, wErr := strconv.Atoi(t[1])
				if wErr != nil {
					return map[string]interface{}{"code": 400, "message": wErr.Error()}, nil
				}

				sms.MonthWages = s.Wages - float64(sms.SumLateTime+sms.SumLeaveEarlyTime)*10 - sms.SumMiner*(s.Wages/float64(CountMonthDay(y, n))) - float64(sms.SumCardShortage*50)

				x.Insert(&sms)
			}

			wg.Done()

			return nil, nil
		}()
	}

	wg.Wait()

	return map[string]interface{}{"code":200, "message":"成功"}, nil

}






























